#include <iostream>
#include <tuple>
#include <fstream>
#include <cstring>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <dirent.h>
#include <stack>
#include <pthread.h>
#include <mutex>

#include "parser.hpp"
#include "count.hpp"

using namespace std;

// Save fileName in a stack rather than a vector
auto file_list = stack<string>(); 
int allcnts, finished;
// init the lock
pthread_mutex_t filemutex, printmutex;
pthread_cond_t cond;

void *work(void *rank)
{
    //thread idx
    long my_rank = (long)rank;

    while (1)
    {
        pthread_mutex_lock(&filemutex);
        if (finished)
        {
          if (file_list.empty())
          {
            pthread_mutex_unlock(&filemutex);
            return NULL;
          }
          string fileName = file_list.top();
          file_list.pop();
          pthread_cond_signal(&cond);
          pthread_mutex_unlock(&filemutex);
          int lineCnts = CountLine(fileName);
          
          pthread_mutex_lock(&printmutex);
          allcnts += lineCnts;
          // cout << (pthread_self()) << ":" << lineCnts << "\t" << fileName << endl;
          printf("%lu:%4d\t%s\n",(unsigned long)pthread_self(), lineCnts, fileName.c_str());
          pthread_mutex_unlock(&printmutex);
        }
        else{
          while (pthread_cond_wait(&cond, &filemutex) != 0);
          if (file_list.empty())
          {
            pthread_mutex_unlock(&filemutex);
            return NULL;
          }
          string fileName = file_list.top();
          file_list.pop();
          pthread_cond_signal(&cond);
          pthread_mutex_unlock(&filemutex);
          int lineCnts = CountLine(fileName);
          
          pthread_mutex_lock(&printmutex);
          allcnts += lineCnts;
          // cout << (pthread_self()) << ":" << lineCnts << "\t" << fileName << endl;
          printf("%lu:%4d\t%s\n",(unsigned long)pthread_self(), lineCnts, fileName.c_str());
          pthread_mutex_unlock(&printmutex);
        }
    }
}

int main(int argc, char *argv[]) {

    auto args = parse_args(argc, argv);
    DIR* pDir;
    struct dirent* myDirent;
    struct stat s;
    char allName[1024];

    // add clock time
    clock_t start,finish;
	  double totaltime;
	  start=clock();

    for (int i = 1; i < argc; ++i) {
      string t_arg = string(argv[i]);
      if (t_arg == "-A" || t_arg == "--all" || t_arg == "-b" || t_arg == "-r" || t_arg == "-s" || t_arg == "-h" || t_arg == "--help" || t_arg == "-t") // add thread
        continue;
      else if (stat(argv[i],&s)==0){
        if(s.st_mode & S_IFDIR){ // Judge whether it's a file or a dir without opening the dir
          if (args.recursive){ // Recursive searching
            GetListR(argv[i], file_list, args.suffix);
          }
          else{ // Non-recursive searching
            pDir = opendir(argv[i]);
            while((myDirent = readdir(pDir)) != NULL)
	          {
		          if(strcmp(myDirent->d_name, ".") == 0 || strcmp(myDirent->d_name, "..") == 0)
		            {
			            continue;
		            } 
              if (myDirent->d_type == 8){
                sprintf(allName, "%s/%s", argv[i], myDirent->d_name);
                string fileName = allName;
                string fsuffix = fileName.substr(fileName.find_last_of('.') + 1); // Get suffix without '.'
                if (args.suffix == "" || strcmp(fsuffix.c_str(), args.suffix.c_str()) == 0)
                  file_list.emplace(fileName);   
                }
            }
        }
        }
        else if (s.st_mode & S_IFREG){ // It's a regular file
          string fileName = argv[i];
          string fsuffix = fileName.substr(fileName.find_last_of('.') + 1);
          if (args.suffix == "" || strcmp(fsuffix.c_str(), args.suffix.c_str()) == 0)
            file_list.emplace(fileName);}     
        else{
          cout<<"Not a file or a directory!"<<endl;
        }
      }
      else{
        continue;
      }
    }

    // Count lines and print fileName with multi-threads

    if(!args.help){
    cout << "line\tfile" << endl;
    cout<< "----\t----" << endl;
    pthread_mutex_init(&filemutex, NULL);
    pthread_mutex_init(&printmutex, NULL);
    pthread_cond_init(&cond, NULL);
    vector<pthread_t> threadPool;
    threadPool.reserve(args.thread);

    //Specify threads
    for (long i = 0; i < args.thread; ++i) {
        pthread_create(&threadPool[i], NULL, work, (void *)i);
    }
    finished = 1;
    for (long i = 0; i < args.thread; i++)
    {
        pthread_cond_broadcast(&cond);
        // wait for thread_i to end
        pthread_join(threadPool[i], NULL);
    }
    pthread_mutex_destroy(&filemutex);
    pthread_mutex_destroy(&printmutex);
    pthread_cond_destroy(&cond);
    cout << allcnts << "\ttotal" <<endl;
    }  
    // add clock time
    finish=clock();
   	totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
   	cout<<"\nRunning time: "<<totaltime<<" seconds."<<endl;
    return 0;
}
